﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ParallelRuntimeLibrary.Internal;

namespace ParallelRuntimeLibrary.Collections
{
    /// <summary>
    /// Represend concurrent enabled last-in, first-out (LIFO) collection of object.
    /// </summary>
    /// <typeparam name="T">Specifies the type of elements in the Stack</typeparam>
    public class ConcurrentStack<T> : IConcurrentCollection<T>
    {
        private ConcurrentModes mConcurrentMode;
        private bool mMaintainCount;
        private int mCount;
        private SingleLinkedListNode<T> mHead;

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Class Constructor

        public ConcurrentStack(): this(false, ConcurrentModes.Lock)
        {
        }

        public ConcurrentStack(bool maintainCount, ConcurrentModes concurrentMode)
        {
            mMaintainCount = maintainCount;
            mConcurrentMode = concurrentMode;
        }

        #endregion

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/
        
        #region Properties

        public int Count
        {
            get
            {
                if (mMaintainCount) return mCount;

                int r = 0;
                for (var tmp = mHead; tmp != null; tmp = tmp.mNext, ++r)
                {
                }

                return r;
            }
        }

        public ConcurrentModes ConcurrentMode
        {
            get { return mConcurrentMode; }
            set { mConcurrentMode = value; }
        }

        public bool MaintainCount
        {
            get { return mMaintainCount; }
            set
            {
                if (value)
                {
                    for (var tmp = mHead; tmp != null; tmp = tmp.mNext, ++mCount)
                    { }
                }
                mMaintainCount = value;
            }
        }

        #endregion

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Stack Operations

        public void Push(T value)
        {
            SingleLinkedListNode<T> tmp = new SingleLinkedListNode<T>(value);

            if (mConcurrentMode == ConcurrentModes.None)
            {
                #region None

                tmp.mNext = mHead;
                mHead = tmp;
                if (mMaintainCount) mCount++;

                #endregion
            }
            else if (mConcurrentMode == ConcurrentModes.Lock)
            {
                #region Lock

                lock (mHead)
                {
                    tmp.mNext = mHead;
                    mHead = tmp;
                    if (mMaintainCount) mCount++;
                }

                #endregion
            }
            else
            {
                #region freeLock

                do
                {
                    tmp.mNext = mHead;
                    if (Interlocked.CompareExchange(ref mHead, tmp, tmp.mNext) == tmp.mNext)
                    {
                        if (mMaintainCount) Interlocked.Increment(ref mCount);
                        return;
                    }
                    else
                    {
                        tmp.mNext = mHead;
                        if (Interlocked.CompareExchange(ref mHead, tmp, tmp.mNext) == tmp.mNext)
                        {
                            if (mMaintainCount) Interlocked.Increment(ref mCount);
                            return;
                        }
                        else
                            NativeMethod.SwitchToThread();
                    }

                } while (true);

                #endregion
            }
        }

        public void PushRange(IEnumerable<T> values)
        {
            SingleLinkedListNode<T> tmp = null;
            foreach (var item in values)
            {
                if (tmp == null) tmp = new SingleLinkedListNode<T>(item);
                else
                {
                    SingleLinkedListNode<T> tmp2 = new SingleLinkedListNode<T>(item);
                    tmp.mNext = tmp2;
                    tmp = tmp2;
                }
            }

            if (mConcurrentMode == ConcurrentModes.None)
            {
                #region None

                tmp.mNext = mHead;
                mHead = tmp;
                if (mMaintainCount) mCount++;

                #endregion
            }
            else if (mConcurrentMode == ConcurrentModes.Lock)
            {
                #region Lock

                lock (mHead)
                {
                    tmp.mNext = mHead;
                    mHead = tmp;
                    if (mMaintainCount) mCount++;
                }

                #endregion
            }
            else
            {
                #region freeLock

                do
                {
                    tmp.mNext = mHead;
                    if (Interlocked.CompareExchange(ref mHead, tmp, tmp.mNext) == tmp.mNext)
                    {
                        if (mMaintainCount) Interlocked.Increment(ref mCount);
                        return;
                    }
                    else
                    {
                        tmp.mNext = mHead;
                        if (Interlocked.CompareExchange(ref mHead, tmp, tmp.mNext) == tmp.mNext)
                        {
                            if (mMaintainCount) Interlocked.Increment(ref mCount);
                            return;
                        }
                        else
                            NativeMethod.SwitchToThread();
                    }

                } while (true);

                #endregion
            }
        }

        public bool Pop(out T value)
        {
            if (mConcurrentMode == ConcurrentModes.None)
            {
                #region None

                if (mHead != null)
                {
                    value = mHead.mValue;
                    mHead = mHead.mNext;
                    if (mMaintainCount) mCount--;
                    return true;
                }
                else
                {
                    value = default(T);
                    return false;
                }

                #endregion
            }
            else if (mConcurrentMode == ConcurrentModes.Lock)
            {
                #region Lock

                lock (mHead)
                {
                    if (mHead != null)
                    {
                        value = mHead.mValue;
                        mHead = mHead.mNext;
                        if (mMaintainCount) mCount--;
                        return true;
                    }
                    else
                    {
                        value = default(T);
                        return false;
                    }
                }

                #endregion
            }
            else
            {
                #region FreeLock

                SingleLinkedListNode<T> OldFirst;

                do
                {
                    OldFirst = mHead;
                    if ((OldFirst != null) && Interlocked.CompareExchange(ref mHead, OldFirst.mNext, OldFirst) == OldFirst)
                    {
                        if (mMaintainCount) Interlocked.Decrement(ref mCount);
                        value = OldFirst.mValue;
                        return true;
                    }
                    else
                    {
                        OldFirst = mHead;
                        if (OldFirst == null)
                        {
                            value = default(T);
                            return false;
                        }
                        else if (Interlocked.CompareExchange(ref mHead, OldFirst.mNext, OldFirst) == OldFirst)
                        {
                            if (mMaintainCount) Interlocked.Decrement(ref mCount);
                            value = OldFirst.mValue;
                            return true;
                        }
                        else
                            NativeMethod.SwitchToThread();
                    }

                } while (true);

                #endregion
            }
        }

        public bool Peek(out T value)
        {
            SingleLinkedListNode<T> tmp = mHead;
            if (tmp != null)
            {
                value = tmp.mValue;
                return true;
            }
            else
            {
                value = default(T);
                return false;
            }
        }

        #endregion

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        #region IConcurrentCollection<T> Members

        public bool Add(T item)
        {
            Push(item);
            return true;
        }

        public bool Remove(out T item)
        {
            return Pop(out item);
        }

        public T[] ToArray()
        {
            T[] R = new T[Count];
            int r = 0;
            for (var tmp = mHead; tmp != null && r < R.Length; tmp = tmp.mNext, ++r)
                R[r] = tmp.mValue;

            return R;
        }

        #endregion

    }

    
}
